package com.seleniti.enpareja.web.desktop.client.ui.view;

import static com.google.gwt.dom.client.BrowserEvents.CLICK;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.ioc.commons.extra.gwt.extensions.renderer.client.HasLabelRenderer;
import org.ioc.commons.impl.gwt.client.ui.HasEnabledWrapper;
import org.ioc.commons.impl.gwt.client.ui.HasValueWrapper;
import org.ioc.commons.impl.gwt.client.ui.ViewComposite;
import org.ioc.commons.impl.gwt.shared.common.util.WrappedDeckPanelAsEnum;
import org.ioc.commons.ui.HasValue;
import org.ioc.commons.ui.HasVisibility;
import org.ioc.commons.ui.impl.HasValueAdapter;
import org.ioc.commons.ui.impl.HasVisibilityAdapter;
import org.ioc.commons.ui.impl.OnChangeCommand;
import org.ioc.commons.utils.Format;
import org.ioc.commons.utils.FormatterLogger;

import com.google.gwt.cell.client.AbstractSafeHtmlCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.HeadingElement;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.safehtml.shared.SafeUri;
import com.google.gwt.text.shared.AbstractSafeHtmlRenderer;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.CellList;
import com.google.gwt.user.client.ui.DeckLayoutPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Html5DateBox;
import com.google.gwt.user.client.ui.Html5TextBox;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.ToggleButton;
import com.google.gwt.user.client.ui.ValueListBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.datepicker.client.CalendarUtil;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.NoSelectionModel;
import com.google.gwt.view.client.ProvidesKey;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.seleniti.enpareja.controller.model.integration.database.domain.WorkShift;
import com.seleniti.enpareja.controller.model.integration.database.domain.WorkShift.Shift;
import com.seleniti.enpareja.controller.ui.view.WorkShiftsView;
import com.seleniti.enpareja.controller.ui.view.WorkShiftsView.Action;
import com.seleniti.enpareja.controller.ui.view.WorkShiftsView.Event;
import com.seleniti.enpareja.controller.ui.view.WorkShiftsView.Operation;
import com.seleniti.enpareja.controller.ui.view.WorkShiftsView.Presenter;
import com.seleniti.enpareja.controller.ui.view.WorkShiftsViewPresenter;
import com.seleniti.enpareja.web.desktop.client.ui.cell.EnParejaTemplate;
import com.seleniti.enpareja.web.desktop.client.ui.resources.CommonResources;
import com.seleniti.enpareja.web.desktop.client.ui.widget.Checker;
import com.seleniti.enpareja.web.desktop.client.ui.widget.Checker.SelectionMode;

public class WorkShiftsViewWidget extends EnParejaBaseView<Event, Operation, Action, Presenter> implements WorkShiftsView {

	private static final FormatterLogger logger = FormatterLogger.getLogger(WorkShiftsViewWidget.class);

	private static TemplateUiBinder uiBinder = GWT.create(TemplateUiBinder.class);

	interface TemplateUiBinder extends UiBinder<Widget, WorkShiftsViewWidget> {
	}

	private static EnParejaTemplate template = GWT.create(EnParejaTemplate.class);

	public static interface Style extends CssResource {
		String container();

		String columnsHeader();

		String dialog();

		String today();

		String undefined();

		String morning();

		String evening();

		String night();

		String split_shift();

		String off();

		String img();

		String label();

		String comment();
	}

	@UiField(provided = true)
	CellList<WorkShift> cellList;

	@UiField
	Style style;

	@UiField
	ToggleButton btnCalendar;

	@UiField
	ToggleButton btnDelete;

	// @UiField
	// HTML lblToolInfo;

	@UiField
	CommonResources common;

	@UiField(provided = true)
	Checker<WorkShift> checker;

	@UiField
	VerticalPanel doneSettingsPanel;

	@UiField
	Html5DateBox date;

	@UiField(provided = true)
	ValueListBox<Shift> shiftListMember1;

	@UiField(provided = true)
	ValueListBox<Shift> shiftListMember2;

	@UiField(provided = true)
	ValueListBox<Shift> calendarShiftListMember1;

	@UiField(provided = true)
	ValueListBox<Shift> calendarShiftListMember2;

	@UiField(provided = true)
	ValueListBox<Shift> rangeDateShiftListMember1;

	@UiField(provided = true)
	ValueListBox<Shift> rangeDateShiftListMember2;

	@UiField
	Html5DateBox filterFrom;

	@UiField
	Html5DateBox filterTo;

	@UiField
	DeckLayoutPanel mainPanel;

	@UiField
	Label member1Name;

	@UiField
	HeadingElement headingMember1;

	@UiField
	HeadingElement calendarHeadingMember1;

	@UiField
	HeadingElement rangeDateHeadingMember1;

	@UiField
	Label member2Name;

	@UiField
	HeadingElement headingMember2;

	@UiField
	HeadingElement calendarHeadingMember2;

	@UiField
	HeadingElement rangeDateHeadingMember2;

	@UiField
	Html5TextBox commentMember1;

	@UiField
	Html5TextBox commentMember2;

	@UiField
	Panel member1Panel;

	@UiField
	Panel member2Panel;

	private ListDataProvider<WorkShift> listDataProvider;

	private boolean loading;

	private ProvidesKey<WorkShift> keyProvider;

	private ActionMode actionMode;

	private boolean enabled = true;

	private WrappedDeckPanelAsEnum<Board> boardManager;

	public WorkShiftsViewWidget() {
		this.presenter = new WorkShiftsViewPresenter(this, eventbus, operationManager, actionController);

		this.keyProvider = new ProvidesKey<WorkShift>() {

			@Override
			public Object getKey(WorkShift item) {
				return item != null ? item.getWorkShiftId() : null;
			}
		};
		SafeHtmlRenderer<WorkShift> renderer = new AbstractSafeHtmlRenderer<WorkShift>() {

			@Override
			public SafeHtml render(WorkShift object) {
				if (object != null) {
					SafeHtmlBuilder shb = new SafeHtmlBuilder();

					DateTimeFormat dtf = DateTimeFormat.getFormat(PredefinedFormat.DATE_FULL);
					boolean today = object.getDate() != null ? CalendarUtil.isSameDate(new Date(), object.getDate())
							: false;

					shb.append(template.div(
							common.style().columnHeader(),
							SafeHtmlUtils.fromTrustedString(object.getDate() != null ? (today ? "<b>HOY </b>" : "")
									+ Format.capitalize(dtf.format(object.getDate())) : "-- sin fecha --")));

					Shift shiftMember1 = object.getShiftMember1();
					appendMemberTemplate(shb, shiftMember1, common.style().member1(), false,
							object.getCommentsMember1());

					Shift shiftMember2 = object.getShiftMember2();
					appendMemberTemplate(shb, shiftMember2, common.style().member2(), true, object.getCommentsMember2());

					return template.listElement(today ? style.today() : "", shb.toSafeHtml());
				} else {
					return SafeHtmlUtils.fromString("-- no element --");
				}
			}
		};
		this.cellList = new CellList<WorkShift>(new AbstractSafeHtmlCell<WorkShift>(renderer, "click") {

			@Override
			protected void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml data, SafeHtmlBuilder sb) {
				if (data != null) {
					sb.append(data);
				}
			}

			public void onBrowserEvent(Context context, Element parent, WorkShift value, NativeEvent event,
					ValueUpdater<WorkShift> valueUpdater) {
				super.onBrowserEvent(context, parent, value, event, valueUpdater);
				if (CLICK.equals(event.getType())) {
					EventTarget eventTarget = event.getEventTarget();
					if (!Element.is(eventTarget)) {
						return;
					}
					if (parent.getFirstChildElement().isOrHasChild(Element.as(eventTarget))) {
						// Ignore clicks that occur outside of the main element.
						if (valueUpdater != null) {
							valueUpdater.update(value);
						}
					}
				}
			}

		}, keyProvider);

		this.cellList.setPageSize(Integer.MAX_VALUE);

		this.cellList.setValueUpdater(new ValueUpdater<WorkShift>() {

			@Override
			public void update(WorkShift value) {
				if (getActionMode() == ActionMode.MODIFY_ELEMENT) {
					logger.info("Clicked meal {0}", value);
					showModifyForm(value);
				}
			}
		});

		List<Shift> shiftListValues = new ArrayList<>(Arrays.asList(Shift.values()));
		shiftListValues.add(0, null);

		this.shiftListMember1 = new ValueListBox<Shift>(new HasLabelRenderer<Shift>("-- No definido --"));
		this.shiftListMember1.setAcceptableValues(shiftListValues);

		this.shiftListMember2 = new ValueListBox<Shift>(new HasLabelRenderer<Shift>("-- No definido --"));
		this.shiftListMember2.setAcceptableValues(shiftListValues);

		this.calendarShiftListMember1 = new ValueListBox<Shift>(new HasLabelRenderer<Shift>("-- No definido --"));
		this.calendarShiftListMember1.setAcceptableValues(shiftListValues);

		this.calendarShiftListMember2 = new ValueListBox<Shift>(new HasLabelRenderer<Shift>("-- No definido --"));
		this.calendarShiftListMember2.setAcceptableValues(shiftListValues);

		this.rangeDateShiftListMember1 = new ValueListBox<Shift>(new HasLabelRenderer<Shift>("-- No definido --"));
		this.rangeDateShiftListMember1.setAcceptableValues(shiftListValues);

		this.rangeDateShiftListMember2 = new ValueListBox<Shift>(new HasLabelRenderer<Shift>("-- No definido --"));
		this.rangeDateShiftListMember2.setAcceptableValues(shiftListValues);

		HTML empty = new HTML("Ningún elemento en tu lista");
		this.cellList.setEmptyListWidget(empty);

		this.cellList.setSelectionModel(new NoSelectionModel<WorkShift>(keyProvider));

		this.listDataProvider = new ListDataProvider<WorkShift>();
		this.listDataProvider.addDataDisplay(cellList);

		this.checker = new Checker<WorkShift>(100);
		this.checker.addDataDisplay(cellList);

		initWidget(uiBinder.createAndBindUi(this));

		empty.setStyleName(common.style().emptyList());

		this.boardManager = new WrappedDeckPanelAsEnum<Board>(mainPanel, Board.values());
		this.boardManager.setValue(Board.MAIN_LIST);

		// CaptionImpl caption = new CaptionImpl();
		// caption.setHTML(template.caption(common.style().caption(),
		// SafeHtmlUtils.fromString("Comida:")));
		// this.formDialog = new DialogBox(false, false, caption);
		// this.formDialog.setWidget(doneSettingsPanel);
		// this.formDialog.setSize("100%", "100%");
		// this.formDialog.addStyleName(common.style().dialog() + " " +
		// style.dialog());
	}

	@Override
	public List<WorkShift> workshiftList() {
		return this.listDataProvider.getList();
	}

	@UiHandler("btnCancel")
	void onCancelButtonClicked(ClickEvent evt) {
		this.hideModificationForm();
	}

	@UiHandler("btnCancel2")
	void onCancelButton2Clicked(ClickEvent evt) {
		this.btnCalendar.setValue(false, true);
	}

	@UiHandler("btnCancel3")
	void onCancelButton3Clicked(ClickEvent evt) {
		this.btnCalendar.setValue(false, true);
	}

	@UiHandler("btnOk")
	void onOkButtonClicked(ClickEvent evt) {
		this.saveModificationForm();
	}

	@UiHandler("filterFrom")
	void onFilterFromValueChanged(ValueChangeEvent<Date> evt) {
		this.presenter.onFilteDateChanged();
	}

	@UiHandler("filterTo")
	void onFilterToValueChanged(ValueChangeEvent<Date> evt) {
		this.presenter.onFilteDateChanged();
	}

	@UiHandler("btnCalendar")
	void onCalendarButtonToggled(ValueChangeEvent<Boolean> evt) {
		boolean enabled = Boolean.TRUE.equals(evt.getValue());
		this.setActionMode(ActionMode.MODIFY_ELEMENT);

		if (enabled) {
			this.mainPanel.showWidget(2);
			btnDelete.setDown(false);
		} else {
			boardManager.setValue(Board.MAIN_LIST);
		}
	}

	@UiHandler("btnDelete")
	void onDeleteButtonToggled(ValueChangeEvent<Boolean> evt) {
		boolean enabled = Boolean.TRUE.equals(evt.getValue());
		this.setActionMode(enabled ? ActionMode.DELETE_ELEMENT : ActionMode.MODIFY_ELEMENT);

		if (enabled) {
			btnCalendar.setValue(false, true);
		}
	}

	@UiHandler("checker")
	void onCheckerSelectionChanged(SelectionChangeEvent evt) {
		presenter.onSelectionChanged();
	}

	@Override
	public void setLoading(boolean loading) {
		this.loading = loading;
		btnCalendar.setEnabled(!loading);
		btnDelete.setEnabled(!loading);
	}

	@Override
	public boolean isLoading() {
		return this.loading;
	}

	@Override
	public void setActionMode(ActionMode mode) {
		this.actionMode = mode;
		switch (mode) {
		case DELETE_ELEMENT:
			checker.setSelectionMode(SelectionMode.Multiselection);
			checker.setVisible(true);
			btnDelete.setDown(true);
			btnCalendar.setDown(false);
			boardManager.setValue(Board.MAIN_LIST);
			break;
		case MODIFY_ELEMENT:
			checker.setVisible(false);
			btnDelete.setDown(false);
			// btnCalendar.setDown(false);
			// mainPanel.showWidget(2);
			break;
		// checker.setSelectionMode(SelectionMode.SingleSelection);
		// checker.setVisible(true);
		// btnDelete.setDown(false);
		// btnModify.setDown(true);
		// break;
		}

		this.presenter.onActionModeChanged();
	}

	@Override
	public ActionMode getActionMode() {
		return actionMode;
	}

	@Override
	public Set<WorkShift> getSelection() {
		Set<WorkShift> selection = new HashSet<WorkShift>();
		boolean selected[] = this.checker.getSelection();
		for (int i = 0; i < selected.length; i++) {
			boolean elmSelected = selected[i];

			if (elmSelected) {
				selection.add(this.workshiftList().get(i));
			}
		}

		return selection;
	}

	@Override
	public boolean isEnabled() {
		return this.enabled;
	}

	@Override
	public void setEnabled(boolean enabled) {
		this.enabled = true;
		HasEnabledWrapper.setEnabled(enabled, btnDelete);
		this.setTitle(enabled ? "" : "¡Asociate a tu pareja antes de empezar a usar todas las opciones!");
	}

	@Override
	public HasValue<Date> date() {
		return HasValueWrapper.from(date, true, this);
	}

	@Override
	public void saveModificationForm() {
		this.presenter.onWorkShiftModified();
	}

	@Override
	public void showModifyForm(WorkShift element) {
		this.date.setValue(element.getDate());
		this.shiftListMember1.setValue(element.getShiftMember1());
		this.shiftListMember2.setValue(element.getShiftMember2());
		this.commentMember1.setText(element.getCommentsMember1());
		this.commentMember2.setText(element.getCommentsMember2());
		this.boardManager.setValue(Board.EDIT_FORM);
		// this.formDialog.center();
	}

	@Override
	public void hideModificationForm() {
		// this.formDialog.hide();
		this.boardManager.setValue(Board.MAIN_LIST);
	}

	@Override
	public HasValue<Date> filterToDate() {
		return HasValueWrapper.from(filterTo, true, this);
	}

	@Override
	public HasValue<Date> filterFromDate() {
		return HasValueWrapper.from(filterFrom, true, this);
	}

	@Override
	public HasValue<Shift> shiftMember1() {
		return HasValueWrapper.from(shiftListMember1, true, this);
	}

	@Override
	public HasValue<Shift> shiftMember2() {
		return HasValueWrapper.from(shiftListMember2, true, this);
	}

	@Override
	public HasValue<String> commentMember1() {
		return HasValueWrapper.from(commentMember1, true, this);
	}

	@Override
	public HasValue<String> commentMember2() {
		return HasValueWrapper.from(commentMember2, true, this);
	}

	@Override
	public HasValue<String> member1Name() {
		return HasValueAdapter.<String> forField("member1Name", this).onChange(new OnChangeCommand<String>() {

			@Override
			public void execute(String name) {
				member1Name.setText(name);
				headingMember1.setInnerText(name);
				calendarHeadingMember1.setInnerText(name);
				rangeDateHeadingMember1.setInnerText(name);
			}
		});
	}

	@Override
	public HasValue<String> member2Name() {
		return HasValueAdapter.<String> forField("member2Name", this).onChange(new OnChangeCommand<String>() {

			@Override
			public void execute(String name) {
				member2Name.setText(name);
				headingMember2.setInnerText(name);
				calendarHeadingMember2.setInnerText(name);
				rangeDateHeadingMember2.setInnerText(name);
			}
		});
	}

	private void appendMemberTemplate(SafeHtmlBuilder shb, Shift shift, String memberStyle, boolean imageAtRight,
			String comment) {
		if (shift == null) {
			shb.append(template.div(memberStyle + " " + style.undefined(), SafeHtmlUtils.fromString("")));
		} else {
			SafeHtml label = template.span("", shift.getLabel());
			if (comment != null) {
				SafeHtmlBuilder commentShb = new SafeHtmlBuilder();

				commentShb.append(label);
				commentShb.append(template.div(memberStyle + " " + style.comment(), SafeHtmlUtils.fromString(comment)));

				label = template.div("", commentShb.toSafeHtml());

			}

			SafeUri icon = null;
			String className = null;
			switch (shift) {
			case MORNING:
				icon = common.sunIcon().getSafeUri();
				className = memberStyle + " " + style.morning();
				break;
			case EVENING:
				icon = common.coffeeIcon().getSafeUri();
				className = memberStyle + " " + style.evening();
				break;
			case NIGHT:
				icon = common.moonIcon().getSafeUri();
				className = memberStyle + " " + style.night();
				break;
			case OFF:
				icon = common.bedIcon().getSafeUri();
				className = memberStyle + " " + style.off();
				break;
			case SPLIT_SHIFT:
				icon = common.splitShiftIcon().getSafeUri();
				className = memberStyle + " " + style.split_shift();
				break;
			default:
				className = memberStyle + " " + style.undefined();
				return;
			}

			if (icon != null) {
				if (imageAtRight) {
					shb.append(template.hDivImg(icon, label, className, style.label(), style.img()));
				} else {
					shb.append(template.hImgDiv(icon, label, className, style.label(), style.img()));
				}
			} else {
				shb.append(template.div(className, label));
			}

		}
	}

	@Override
	public HasValue<Board> boardShown() {
		return this.boardManager;
	}

	@Override
	public boolean goBoardBack() {
		return this.presenter.onGoBoardBack();
	}

	@Override
	public HasVisibility member1ActionsVisible() {
		return HasVisibilityAdapter.forField("member1ActionsVisible", this).onChange(new OnChangeCommand<Boolean>() {

			@Override
			public void execute(Boolean value) {
				member1Panel.setVisible(value);
				setVisible(member1Name.getElement().getParentElement(), value);
			}
		});
	}

	@Override
	public HasVisibility member2ActionsVisible() {
		return HasVisibilityAdapter.forField("member2ActionsVisible", this).onChange(new OnChangeCommand<Boolean>() {

			@Override
			public void execute(Boolean value) {
				member2Panel.setVisible(value);
				setVisible(member2Name.getElement().getParentElement(), value);
			}
		});
	}
}
